package cate.game.res;

import cate.common.annotation.SplitParse;
import cate.common.table.d.GDCamp;
import cate.common.table.d.GDObj;
import cate.common.table.d.GDProp;
import cate.common.table.item.IBagItemTpl;
import cate.common.table.item.ItemBaseRow;
import cate.common.table.item.num.NumResRow;
import cate.common.util.GameResult;
import cate.game.GameBody;
import cate.game.activity.customwarorder.RoleCustomWarOrder;
import cate.game.activity.hangdrop.HangDropActivity;
import cate.game.activity.luckybag.LuckyBagActivity;
import cate.game.activity.warorder.RoleWarOrder;
import cate.game.client.msg.MergeMsgSender;
import cate.game.log.GameLoggerFactory;
import cate.game.role.Role;
import cate.game.role.bag.awakenfairy.AwakenFairy;
import cate.game.role.bag.equip.Equip;
import cate.game.role.bag.fairy.Fairy;
import cate.game.role.bag.furniture.Furniture;
import cate.game.role.bag.hero.Hero;
import cate.game.role.bag.item.BaseBagItem;
import cate.game.role.bag.kitchenware.Kitchenware;
import easy.java.dev.note.NoteField;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@SuppressWarnings("unchecked")
public class MixResItem {

    public MixResItem() {
    }

    public MixResItem(byte type, int tid, long num) {
        this.type = type;
        this.tid = tid;
        this.num = num;
    }

    public MixResItem(BaseBagItem instance) {
        this.instance = instance;
        this.type = instance.type;
        this.tid = instance.tid;
        this.num = instance.num;
    }

    public static MixResItem parse(String rewardStr) {
        String[] arr = rewardStr.split(":");
        return new MixResItem(Byte.parseByte(arr[0]), Integer.parseInt(arr[1]), Integer.parseInt(arr[2]));
    }

    @NoteField("对象大类型")
    @SplitParse(order = 0)
    public byte type;

    @NoteField(value = "限定条件",
            detail = "比如道具的限定条件是道具的模板ID，"
                    + "数值资源类的限定条件是数值资源枚举（GDObj.Num.XXX）,"
                    + "指定星级英雄的限定条件是星级，"
                    + "指定原型英雄的限定条件是原型ID")
    @SplitParse(order = 1)
    public int tid;

    @NoteField("数量")
    @SplitParse(order = 2)
    public long num;

    /**
     * 实例数据
     */
    public BaseBagItem instance;

    /**
     * 获取模板ID
     */
    public int tid() {
        return this.tid;
    }

    public void tid(int value) {
        this.tid = value;
    }

    /**
     * 获取数值类型
     */
    public int numType() {
        return tid;
    }

    /**
     * 获取星级
     */
    public int star() {
        return this.tid;
    }

    /**
     * 获取原型ID
     */
    public int protoId() {
        return this.tid;
    }

    /**
     * 检查消耗是否足够
     */
    public GameResult consumeCheck(Role role, CostSelect cs) {
        GameResult r = new GameResult();
        if (num < 0) {
            return r.fail("数量有误");
        }
        if (GDObj.Type.NUM == type) {
            boolean enough;
            if (GDObj.Num.EXP == numType()) {
                enough = role.getBase().exp >= num;
            } else {
                enough = role.getBag().numRes.count(tid()) >= num;
            }
            if (!enough) {
                return r.fail(String.format("所需消耗的%s数量不足", GDObj.Num.name(numType())));
            }
        }
        if (GDObj.Type.HERO == type) {
            r = consumeCheckHero(cs, role, null, "请选择指定的英雄哟");
            if (!r.ok()) {
                return r;
            }
        } else if (GDObj.Type.EQUIP == type) {
            if (cs == null) {
                if (role.getBag().getEquip().count(tid()) < num) {
                    return r.fail("所需装备数量不足");
                }
            } else {
                r = consumeCheckEquip(cs, role);
                if (!r.ok()) {
                    return r;
                }
            }
        } else if (GDObj.Type.PROP == type) {
            if (role.getBag().prop.count(tid()) < num) {
                return r.fail("所需道具数量不足");
            }
        } else if (GDObj.Type.HERO_PART == type) {
            if (role.getBag().heroPart.count(tid()) < num) {
                return r.fail("所需碎片数量不足");
            }
        } else if (GDObj.Type.RUNE == type) {
            if (role.getBag().rune.count(tid()) < num) {
                return r.fail("所需徽章数量不足");
            }
        } else if (GDObj.Type.FAIRY == type) {
            r = consumeCheckFairy(cs, role);
            if (!r.ok()) {
                return r;
            }
        } else if (GDObj.Type.HERO_OF_STAR == type) {
            r = consumeCheckHero(cs, role, hero -> hero.getTpl(role.toPlayBuildContext()).star == star(), "请选择指定的英雄哟");
            if (!r.ok()) {
                return r;
            }
        } else if (GDObj.Type.HERO_OF_STAR_CAMP == type) {
            final int starReq = this.tid / 10;
            final int campReq = this.tid % 10;
            r = consumeCheckHero(cs, role,
                    hero -> (hero.getTpl(role.toPlayBuildContext()).star == starReq && hero.getTpl(role.toPlayBuildContext()).camp == campReq), "请选择指定的英雄哟");
            if (!r.ok()) {
                return r;
            }
        } else if (GDObj.Type.HERO_OF_STAR_NIUBI_CAMP == type) {
            r = consumeCheckHero(cs, role,
                    hero -> (hero.getTpl(role.toPlayBuildContext()).star == star()
                            && GDCamp.Type.isNiuBi(hero.getTpl(role.toPlayBuildContext()).camp)), "请选择指定的英雄哟");
            if (!r.ok()) {
                return r;
            }
        } else if (GDObj.Type.HERO_OF_NATURE_CAMP == type) {
            r = consumeCheckHero(cs, role,
                    hero -> (hero.getTpl(role.toPlayBuildContext()).star == star()
                            && GDCamp.Type.isNature(hero.getTpl(role.toPlayBuildContext()).camp)), "请选择指定的英雄哟");
            if (!r.ok()) {
                return r;
            }
        } else if (GDObj.Type.AWAKEN_FAIRY == type) {
            r = consumeCheckAwakenFairy(cs, role);
            if (!r.ok()) {
                return r;
            }
        } else if (GDObj.Type.KITCHENWARE == type) {
            r = consumeCheckKitchenware(cs, role);
            if (!r.ok()) {
                return r;
            }
        } else if (GDObj.Type.GEM == type) {
            if (role.getBag().gem.count(tid()) < num) {
                return r.fail("所需道具数量不足");
            }
        } else if (GDObj.Type.FURNITURE == type) {
            r = consumeCheckFurniture(cs, role);
            if (!r.ok()) {
                return r;
            }
        } else if (GDObj.Type.SPICE == type) {
            if (role.getBag().spice.count(tid()) < num) {
                return r.fail("所需道具数量不足");
            }
        }
        return r;
    }

    private GameResult consumeCheckHero(
            CostSelect cs, Role role, Predicate<Hero> condition, String conditionFailMsg) {
        GameResult r = new GameResult();
        if (cs != null) {
            List<String> uidList = cs.getUidList(type, tid);
            if (uidList == null || uidList.size() < num) {
                return r.fail(String.format("请选择%d个英雄", num));
            }
            for (String uid : uidList) {
                Hero hero = role.getBag().hero.getItem(uid);
                if (hero == null) {
                    return r.fail("有英雄已经消失了呀");
                }
                if (hero.lock.heroReplace) {
                    return r.fail("英雄已被置换锁定啦！");
                } else if (hero.lock.heroShare) {
                    return r.fail("英雄已被世界树锁定啦！");
                } else if (hero.lock.manual) {
                    return r.fail("英雄已被手动锁定啦！");
                } else if (hero.lock.heroFight) {
                    return r.fail("英雄已被上阵锁定啦！");
                } else if (hero.lock.heroPendant) {
                    return r.fail("英雄已被锁定啦！");
                } else if (hero.lock.heroYoke) {
                    return r.fail("英雄已被羁绊锁定啦！");
                } else if (hero.lock.heroContract) {
                    return r.fail("英雄已被灵契台锁定啦！");
                }
                if (condition != null && !condition.test(hero)) {
                    return r.fail(conditionFailMsg);
                }
            }
        } else {
            return r.fail("请选择指定的英雄哟");
        }
        return r.success();
    }

    private GameResult consumeCheckFairy(
            CostSelect cs, Role role) {
        GameResult r = new GameResult();
        if (cs != null) {
            List<String> uidList = cs.getUidList(type, tid);
            if (uidList == null || uidList.size() < num) {
                return r.fail("灵器不足");
            }
            for (String uid : uidList) {
                Fairy fairy = role.getBag().getFairy().getItem(uid);
                if (fairy == null) {
                    return r.fail("有灵器已经消失了呀");
                }
            }
        } else {
            return r.fail("请选择指定的英雄哟");
        }
        return r.success();
    }

    private GameResult<Void> consumeCheckAwakenFairy(
            CostSelect cs, Role role) {
        GameResult<Void> r = new GameResult<>();
        if (cs != null) {
            List<String> uidList = cs.getUidList(type, tid);
            if (uidList == null || uidList.size() < num) {
                return r.fail("觉醒灵器不足");
            }
            for (String uid : uidList) {
                AwakenFairy awakenFairy = role.getBag().getAwakenFairy().getItem(uid);
                if (awakenFairy == null) {
                    return r.fail("有觉醒灵器已经消失了呀");
                }
            }
        } else {
            return r.fail("请选择指定的觉醒灵器哟");
        }
        return r.success();
    }

    private GameResult<Void> consumeCheckKitchenware(
            CostSelect cs, Role role) {
        GameResult<Void> r = new GameResult<>();
        if (cs != null) {
            List<String> uidList = cs.getUidList(type, tid);
            if (uidList == null || uidList.size() < num) {
                return r.fail("厨具数量不足");
            }
            for (String uid : uidList) {
                Kitchenware awakenFairy = role.getBag().kitchenware.getItem(uid);
                if (awakenFairy == null) {
                    return r.fail("有厨具已经消失了呀");
                }
            }
        } else {
            return r.fail("请选择指定的厨具哟");
        }
        return r.success();
    }

    private GameResult<Void> consumeCheckFurniture(
            CostSelect cs, Role role) {
        GameResult<Void> r = new GameResult<>();
        if (cs != null) {
            List<String> uidList = cs.getUidList(type, tid);
            if (uidList == null || uidList.size() < num) {
                return r.fail("手办数量不足");
            }
            for (String uid : uidList) {
                Furniture furniture = role.getBag().furniture.getItem(uid);
                if (furniture == null) {
                    return r.fail("有手办已经消失了呀");
                }
            }
        } else {
            return r.fail("请选择指定的手办哟");
        }
        return r.success();
    }

    private GameResult consumeCheckEquip(
            CostSelect cs, Role role) {
        GameResult r = new GameResult();
        if (cs != null) {
            List<String> uidList = new ArrayList<>();
            if (cs.options != null) {
                for (Option opt : cs.options) {
                    if (opt.type == type && opt.tid == tid && opt.uidList != null && !opt.uidList.isEmpty()) {
                        uidList.addAll(opt.uidList);
                    }
                }
            }
            uidList = uidList.stream().distinct().collect(Collectors.toList());
            if (uidList.size() < num) {
                return r.fail("装备不足");
            }
            for (String uid : uidList) {
                Equip equip = role.getBag().getEquip().getItem(uid);
                if (equip == null) {
                    return r.fail("有装备已经消失了呀");
                }
            }
        } else {
            return r.fail("请选择指定的装备哟");
        }
        return r.success();
    }

    /**
     * 进行扣除消耗(会做消耗检测)
     */
    public GameResult consume(Role role, CostSelect cs, MergeMsgSender sender) {
        GameResult r = consumeCheck(role, cs);
        if (!r.ok()) {
            return r;
        }
        consumeDirect(role, cs, sender);
        return r;
    }

    /**
     * 直接进行扣除消耗
     */
    public void consumeDirect(Role role, CostSelect cs, MergeMsgSender sender) {
        if (GDObj.Type.NUM == type) {
            if (GDObj.Num.EXP == numType()) {
                role.getBase().addExp(-num, sender);
            } else {
                role.getBag().numRes.removeItem(tid(), num, sender);
            }
        } else if (GDObj.Type.EQUIP == type) {
            if (cs == null) {
                role.getBag().getEquip().removeItem(tid(), num, sender);
            } else {
                List<String> uidList = new ArrayList<>();
                if (cs.options != null) {
                    for (Option opt : cs.options) {
                        if (opt.type == type && opt.tid == tid && opt.uidList != null && !opt.uidList.isEmpty()) {
                            uidList.addAll(opt.uidList);
                        }
                    }
                }
                uidList = uidList.stream().distinct().collect(Collectors.toList());
                for (String uid : uidList) {
                    role.getBag().getEquip().removeItem(uid, sender);
                }
            }
        } else if (GDObj.Type.PROP == type) {
            role.getBag().prop.removeItem(tid(), num, sender);
        } else if (GDObj.Type.RUNE == type) {
            role.getBag().rune.removeItem(tid(), num, sender);
        } else if (GDObj.Type.FAIRY == type) {
            List<String> uidList = cs.getUidList(type, tid);
            for (String uid : uidList) {
                role.getBag().getFairy().removeItem(uid, sender);
            }
        } else if (GDObj.Type.FURNITURE == type) {
            if (cs == null) {
                role.getBag().furniture.removeItem(tid(), num, sender);
            } else {
                List<String> uidList = cs.getUidList(type, tid);
                for (String uid : uidList) {
                    role.getBag().furniture.removeItem(uid, sender);
                }
            }
        } else if (GDObj.Type.HERO == type
                || GDObj.Type.HERO_OF_STAR == type
                || GDObj.Type.HERO_OF_STAR_CAMP == type
                || GDObj.Type.HERO_OF_PROTO == type
                || GDObj.Type.HERO_OF_STAR_NIUBI_CAMP == type
                || GDObj.Type.HERO_OF_NATURE_CAMP == type) {
            List<String> uidList = cs.getUidList(type, tid);
            for (String uid : uidList) {
                role.getBag().hero.removeItem(uid, sender);
            }
        } else {
            if (cs != null) {
                List<String> uidList = cs.getUidList(type, tid);
                for (String uid : uidList) {
                    role.getBag().getBag(type).removeItem(uid, sender);
                }
            } else {
                role.getBag().getBag(type).removeItem(tid(), num, sender);
            }
        }
    }

    /**
     * 检查是否可以添加
     */
    public GameResult<Void> addCheck(Role role) {
        GameResult<Void> r = new GameResult<>();
        if (role == null) {
            return r.fail("主角已不存在");
        }
        if (GDObj.Type.NUM == type) {
            return r.success();
        } else if (GDObj.Type.EQUIP == type) {
            return role.getBag().getEquip().addItemCheck(tid(), (int) num);
        } else {
            return role.getBag().getBag(type).addItemCheck(tid(), (int) num);
        }
    }

    /**
     * 添加，并做检查
     */
    public GameResult<Void> add(Role role, MergeMsgSender sender) {
        GameResult<Void> r = addCheck(role);
        if (!r.ok()) {
            return r;
        }
        addDirect(role, sender);
        return r.success();
    }

    /**
     * 直接添加，不做检查
     */
    public void addDirect(Role role, MergeMsgSender sender) {
        if (role == null) {
            return;
        }
        try {
            if (GDObj.Type.NUM == type && GDObj.Num.EXP == numType()) {
                role.getBase().addExp(num, sender);
            } else if (GDObj.Type.NUM == type && GDObj.Num.DAILY_ACTIVE == numType()) {
                role.getTask().daily.addActive(num);
            } else if (GDObj.Type.NUM == type && GDObj.Num.WEEK_ACTIVE == numType()) {
                role.getTask().weekly.addActive(num);
            } else if (GDObj.Type.NUM == type && GDObj.Num.VIP_POINT == numType()) {
                role.getBase().addVipPoint(num);
            } else if (GDObj.Type.NUM == type && GDObj.Num.INFO_VALUE == numType()) {
                role.getFarm().adventure.task.addInformation(num, sender);
            } else if (GDObj.Type.PROP == type && GDProp.Type.HANG_DROP_ACTIVITY_MATERIALS == getPropType(role, tid())) {
                role.getActivity().getEnableList(HangDropActivity.class).forEach(single -> single.addMaterialsNum(tid(), num));
            } else if (GDObj.Type.NUM == type && GDObj.NumSmallType.WAR_ORDER_EXP == getNumSmallType(role, tid())) {
                role.getActivity().getEnableList(RoleWarOrder.class).forEach(single -> single.level.addExpByItem(tid(), num));
                role.getActivity().getEnableList(RoleCustomWarOrder.class).forEach(single -> single.level.addExpByItem(tid(), num));
            }else if (GDObj.Type.NUM == type && GDObj.Num.LUCKY_SCORE == numType()) {
                role.getActivity().getEnableList(LuckyBagActivity.class).forEach(single -> single.addNumByItem(tid(), num));
            } else {
                if (instance != null) {
                    role.getBag().getBag(type).addItem(instance, sender);
                } else {
                    role.getBag().getBag(type).addItem(tid(), num, sender);
                }
            }
        } catch (Exception e) {
            GameLoggerFactory.getLogger(role.getGame(), MixResItem.class).error("物品直接添加失败 type={}, limit={}, num={}", this.type, this.tid, this.num, e);
        }
    }

    /**
     * 获取道具类型
     */
    private int getPropType(Role role, int tid) {
        ItemBaseRow row = role.getGame().table.item.base.get(tid);
        return row == null ? 0 : row.type;
    }

    /**
     * 获取资源小类
     */
    private int getNumSmallType(Role role, int tid) {
        NumResRow row = role.getGame().table.item.numRes.get(tid);
        return row == null ? 0 : row.smallType;
    }

    /**
     * 所有item数量乘以scale倍
     */
    public MixResItem multiply(long scale) {
        this.num = this.num * scale;
        return this;
    }

    public void multiplyDouble(double scale) {
        this.num = (int) Math.floor(this.num * scale);
    }

    public void multiplyDoubleCeil(double scale) {
        this.num = (int) Math.ceil(this.num * scale);
    }

    public MixResItem copy() {
        MixResItem cp = new MixResItem();
        cp.type = this.type;
        cp.tid = this.tid;
        cp.num = this.num;
        return cp;
    }

    @Override
    public String toString() {
        return "" + type + ":" + tid + ":" + num;
    }

    public String description(GameBody game) {
        IBagItemTpl tpl = null;
        if (GDObj.Type.NUM == type) {
            tpl = game.table.item.numRes.get(this.tid());
        } else if (GDObj.Type.HERO == type) {
            tpl = game.table.hero.base.get(this.tid());
        } else if (GDObj.Type.EQUIP == type) {
            tpl = game.table.equip.base.get(this.tid());
        } else if (GDObj.Type.PROP == type) {
            tpl = game.table.item.base.get(this.tid());
        } else if (GDObj.Type.RUNE == type) {
            tpl = game.table.rune.base.get(this.tid());
        } else if (GDObj.Type.FAIRY == type) {
            tpl = game.table.fairy.base.get(this.tid());
        } else if (GDObj.Type.AWAKEN_FAIRY == type) {
            tpl = game.table.awakenFairy.base.get(this.tid());
        } else if (GDObj.Type.KITCHENWARE == type) {
            tpl = game.table.kitchenware.base.get(this.tid());
        }
        if (tpl == null) {
            return "不存在的物品";
        }
        return tpl.getName();
    }
}
